package org.ala.tiktools.rpc.kcp.netty.client.config;

import static io.netty.channel.ChannelOption.IP_TOS;
import static org.ala.tiktools.rpc.kcp.netty.KcpChannelOption.KCP_AUTO_SET_CONV;
import static org.ala.tiktools.rpc.kcp.netty.KcpChannelOption.KCP_DEAD_LINK;
import static org.ala.tiktools.rpc.kcp.netty.KcpChannelOption.KCP_FAST_FLUSH;
import static org.ala.tiktools.rpc.kcp.netty.KcpChannelOption.KCP_FAST_LIMIT;
import static org.ala.tiktools.rpc.kcp.netty.KcpChannelOption.KCP_FAST_RESEND;
import static org.ala.tiktools.rpc.kcp.netty.KcpChannelOption.KCP_INTERVAL;
import static org.ala.tiktools.rpc.kcp.netty.KcpChannelOption.KCP_MERGE_SEGMENT_BUF;
import static org.ala.tiktools.rpc.kcp.netty.KcpChannelOption.KCP_MIN_RTO;
import static org.ala.tiktools.rpc.kcp.netty.KcpChannelOption.KCP_MTU;
import static org.ala.tiktools.rpc.kcp.netty.KcpChannelOption.KCP_NOCWND;
import static org.ala.tiktools.rpc.kcp.netty.KcpChannelOption.KCP_NODELAY;
import static org.ala.tiktools.rpc.kcp.netty.KcpChannelOption.KCP_RCV_WND;
import static org.ala.tiktools.rpc.kcp.netty.KcpChannelOption.KCP_SND_WND;
import static org.ala.tiktools.rpc.kcp.netty.KcpChannelOption.KCP_STREAM;

import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.Map;

import org.ala.tiktools.rpc.kcp.config.RpcKcpConfigInfo;
import org.ala.tiktools.rpc.kcp.netty.NettyKcp;
import org.ala.tiktools.rpc.kcp.netty.client.KcpClientChannel;

import io.netty.channel.ChannelException;
import io.netty.channel.ChannelOption;
import io.netty.channel.DefaultChannelConfig;
import io.netty.channel.FixedRecvByteBufAllocator;

/**
 * @author ala
 * @date 2025-04-15 22:40
 */
public class DefaultKcpClientChannelConfig extends DefaultChannelConfig implements IKcpClientChannelConfig {

    private NettyKcp nkcp;
    private DatagramSocket javaSocket;


    public DefaultKcpClientChannelConfig(KcpClientChannel channel, NettyKcp kcp, DatagramSocket socket) {
        super(channel, new FixedRecvByteBufAllocator(RpcKcpConfigInfo.INSTANCE.getUdpRecvBufferAllocateSize()));
        nkcp = kcp;
        javaSocket = socket;
    }


    @Override
    public Map<ChannelOption<?>, Object> getOptions() {
        return getOptions(
                super.getOptions(),
                KCP_NODELAY, KCP_INTERVAL, KCP_FAST_RESEND, KCP_FAST_LIMIT, KCP_NOCWND, KCP_MIN_RTO, KCP_MTU,
                KCP_RCV_WND, KCP_SND_WND, KCP_STREAM, KCP_DEAD_LINK, KCP_AUTO_SET_CONV, KCP_FAST_FLUSH,
                KCP_MERGE_SEGMENT_BUF, ChannelOption.SO_RCVBUF, ChannelOption.SO_SNDBUF, ChannelOption.SO_REUSEADDR, IP_TOS);
    }

    @Override
    @SuppressWarnings({"unchecked"})
    public <T> T getOption(ChannelOption<T> option) {
        if (option == KCP_NODELAY) {
            return (T) Boolean.valueOf(isNoDelay());
        }
        if (option == KCP_INTERVAL) {
            return (T) Integer.valueOf(getInterval());
        }
        if (option == KCP_FAST_RESEND) {
            return (T) Integer.valueOf(getFastResend());
        }
        if (option == KCP_FAST_LIMIT) {
            return (T) Integer.valueOf(getFastLimit());
        }
        if (option == KCP_NOCWND) {
            return (T) Boolean.valueOf(isNoCwnd());
        }
        if (option == KCP_MIN_RTO) {
            return (T) Integer.valueOf(getMinRto());
        }
        if (option == KCP_MTU) {
            return (T) Integer.valueOf(getMtu());
        }
        if (option == KCP_RCV_WND) {
            return (T) Integer.valueOf(getRcvWnd());
        }
        if (option == KCP_SND_WND) {
            return (T) Integer.valueOf(getSndWnd());
        }
        if (option == KCP_STREAM) {
            return (T) Boolean.valueOf(isStream());
        }
        if (option == KCP_DEAD_LINK) {
            return (T) Integer.valueOf(getDeadLink());
        }
        if (option == KCP_AUTO_SET_CONV) {
            return (T) Boolean.valueOf(isAutoSetConv());
        }
        if (option == KCP_FAST_FLUSH) {
            return (T) Boolean.valueOf(isFastFlush());
        }
        if (option == KCP_MERGE_SEGMENT_BUF) {
            return (T) Boolean.valueOf(isMergeSegmentBuf());
        }
        if (option == ChannelOption.SO_RCVBUF) {
            return (T) Integer.valueOf(getUdpReceiveBufferSize());
        }
        if (option == ChannelOption.SO_SNDBUF) {
            return (T) Integer.valueOf(getUdpSendBufferSize());
        }
        if (option == ChannelOption.SO_REUSEADDR) {
            return (T) Boolean.valueOf(isReuseAddress());
        }
        if (option == IP_TOS) {
            return (T) Integer.valueOf(getUdpTrafficClass());
        }
        return super.getOption(option);
    }

    private int getUdpTrafficClass() {
        try {
            return javaSocket.getTrafficClass();
        } catch (SocketException e) {
            throw new ChannelException(e);
        }
    }

    private boolean isReuseAddress() {
        try {
            return javaSocket.getReuseAddress();
        } catch (SocketException e) {
            throw new ChannelException(e);
        }
    }

    private int getUdpSendBufferSize() {
        try {
            return javaSocket.getSendBufferSize();
        } catch (SocketException e) {
            throw new ChannelException(e);
        }
    }

    private int getUdpReceiveBufferSize() {
        try {
            return javaSocket.getReceiveBufferSize();
        } catch (SocketException e) {
            throw new ChannelException(e);
        }
    }

    @Override
    public <T> boolean setOption(ChannelOption<T> option, T value) {
        validate(option, value);

        if (option == KCP_NODELAY) {
            setNoDelay((Boolean) value);
        } else if (option == KCP_INTERVAL) {
            setInterval((Integer) value);
        } else if (option == KCP_FAST_RESEND) {
            setFastResend((Integer) value);
        } else if (option == KCP_FAST_LIMIT) {
            setFastLimit((Integer) value);
        } else if (option == KCP_NOCWND) {
            setNoCwnd((Boolean) value);
        } else if (option == KCP_MIN_RTO) {
            setMinRto((Integer) value);
        } else if (option == KCP_MTU) {
            setMtu((Integer) value);
        } else if (option == KCP_RCV_WND) {
            setRcvWnd((Integer) value);
        } else if (option == KCP_SND_WND) {
            setSndWnd((Integer) value);
        } else if (option == KCP_STREAM) {
            setStream((Boolean) value);
        } else if (option == KCP_DEAD_LINK) {
            setDeadLink((Integer) value);
        } else if (option == KCP_AUTO_SET_CONV) {
            setAutoSetConv((Boolean) value);
        } else if (option == KCP_FAST_FLUSH) {
            setFastFlush((Boolean) value);
        } else if (option == KCP_MERGE_SEGMENT_BUF) {
            setMergeSegmentBuf((Boolean) value);
        } else if (option == ChannelOption.SO_RCVBUF) {
            setUdpReceiveBufferSize((Integer) value);
        } else if (option == ChannelOption.SO_SNDBUF) {
            setUdpSendBufferSize((Integer) value);
        } else if (option == ChannelOption.SO_REUSEADDR) {
            try {
                javaSocket.setReuseAddress((Boolean) value);
            } catch (SocketException e) {
                throw new RuntimeException(e);
            }
        } else if (option == IP_TOS) {
            setUdpTrafficClass((Integer) value);
        } else {
            return super.setOption(option, value);
        }

        return true;
    }

    private void setUdpTrafficClass(Integer value) {
        try {
            javaSocket.setTrafficClass(value);
        } catch (SocketException e) {
            throw new ChannelException(e);
        }
    }

    private void setUdpSendBufferSize(Integer value) {
        try {
            javaSocket.setSendBufferSize(value);
        } catch (SocketException e) {
            throw new ChannelException(e);
        }
    }

    private void setUdpReceiveBufferSize(Integer value) {
        try {
            javaSocket.setReceiveBufferSize(value);
        } catch (SocketException e) {
            throw new ChannelException(e);
        }
    }


    @Override
    public boolean isNoDelay() { return nkcp.isNoDelay(); }
    @Override
    public IKcpClientChannelConfig setNoDelay(boolean noDelay) {
        nkcp.setNodelay(noDelay);
        return this;
    }

    @Override
    public int getInterval() { return nkcp.getInterval(); }
    @Override
    public IKcpClientChannelConfig setInterval(int interval) {
        nkcp.setInterval(interval);
        return this;
    }

    @Override
    public int getFastResend() { return nkcp.getFastResend(); }
    @Override
    public IKcpClientChannelConfig setFastResend(int fastResend) {
        nkcp.setFastResend(fastResend);
        return this;
    }

    @Override
    public int getFastLimit() { return nkcp.getFastLimit(); }
    @Override
    public IKcpClientChannelConfig setFastLimit(int limit) {
        nkcp.setFastLimit(limit);
        return this;
    }

    @Override
    public boolean isNoCwnd() { return nkcp.isNoCwnd(); }
    @Override
    public IKcpClientChannelConfig setNoCwnd(boolean cwnd) {
        nkcp.setNocwnd(cwnd);
        return this;
    }

    @Override
    public int getMinRto() { return nkcp.getMinRto(); }
    @Override
    public IKcpClientChannelConfig setMinRto(int minRto) {
        nkcp.setMinRto(minRto);
        return this;
    }

    @Override
    public int getMtu() { return nkcp.getMtu(); }
    @Override
    public IKcpClientChannelConfig setMtu(int mtu) {
        nkcp.setMtu(mtu);
        return this;
    }

    @Override
    public int getRcvWnd() { return nkcp.getRcvWnd(); }
    @Override
    public IKcpClientChannelConfig setRcvWnd(int rcvWnd) {
        nkcp.setRcvWnd(rcvWnd);
        return this;
    }

    @Override
    public int getSndWnd() { return nkcp.getSndWnd(); }
    @Override
    public IKcpClientChannelConfig setSndWnd(int sndWnd) {
        nkcp.setSndWnd(sndWnd);
        return this;
    }

    @Override
    public boolean isStream() { return nkcp.isStream(); }
    @Override
    public IKcpClientChannelConfig setStream(boolean stream) {
        nkcp.setStream(stream);
        return this;
    }

    @Override
    public int getDeadLink() { return nkcp.getDeadLink(); }
    @Override
    public IKcpClientChannelConfig setDeadLink(int deadLink) {
        nkcp.setDeadLink(deadLink);
        return this;
    }

    @Override
    public boolean isAutoSetConv() { return nkcp.isAutoSetConv(); }
    @Override
    public IKcpClientChannelConfig setAutoSetConv(boolean autoSetConv) {
        nkcp.setAutoSetConv(autoSetConv);
        return this;
    }

    @Override
    public boolean isFastFlush() { return nkcp.isFastFlush(); }
    @Override
    public IKcpClientChannelConfig setFastFlush(boolean fastFlush) {
        nkcp.setFastFlush(fastFlush);
        return this;
    }

    @Override
    public boolean isMergeSegmentBuf() { return nkcp.isMergeSegmentBuf(); }
    @Override
    public IKcpClientChannelConfig setMergeSegmentBuf(boolean mergeSegmentBuf) {
        nkcp.setMergeSegmentBuf(mergeSegmentBuf);
        return this;
    }
}
